package com.dopios.services.webapp.service;


import com.dopios.services.persistance.dao.AdviceDao;
import com.dopios.services.persistance.domain.CityAdvice;
import com.dopios.services.webapp.utilities.AdviceValidator;
import com.dopios.services.webapp.utilities.TagsManager;
import com.dopios.services.webapp.utilities.Utilities;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;


public class AdviceServiceImpl implements AdviceService {

    private static final Logger logger = Logger.getLogger(AdviceServiceImpl.class);
    public static  List<Map<String, Object>> tags;
    @Autowired
    @Qualifier("adviceDao")
    private AdviceDao adviceDao;

    @Override
    public String handleCityAdvice(CityAdvice cityAdvice) {
        String response = "";
        logger.info("Started handling advice '" + cityAdvice.getAdvice() + "' for city '" + cityAdvice.getCityName() + "'");
        Errors postAdviceErrors = new BeanPropertyBindingResult(cityAdvice, "cityAdvice");
        AdviceValidator adviceValidator = new AdviceValidator();
        adviceValidator.validate(cityAdvice, postAdviceErrors);
        if (postAdviceErrors.hasErrors()) { //Validation errors found.Inform the user and abandon persisting the advice.
            logger.info("Errors were found on posting of advice.");
            List<ObjectError> errors = postAdviceErrors.getAllErrors();
            for (ObjectError error : errors) {
                response = response + "Error: " + error.getDefaultMessage() + "\n";
            }
            return response;
        } else { // The validation was successful. Store the advice.
            try {
                if (adviceDao.checkUserExists(cityAdvice.getUserAddedId()) && adviceDao.checkCityExists(cityAdvice.getCityName())) {
                    logger.info("Storing advice...");
                    //get the tags store them in the advice pbject and do a transactional queryto store all of them.
                    if(tags==null) {
                    tags = adviceDao.getTagsKeywords();
                    }
                    List<Map<String, Object>> matchedTags = TagsManager.getMatchedTags(cityAdvice.getAdvice(), tags);
                    cityAdvice.setMatchingKeywords(matchedTags);
                    int id = adviceDao.addCityAdvice(cityAdvice);
                    if (matchedTags.size() > 0) {
                        adviceDao.addTagsForCityAdvice(id, matchedTags);
                    }
                    logger.info("Advice stored.");
                } else {
                    response = "Error: " + " Either the user or the city do not exist. Please check the parameters.";
                    logger.error(" Either the user or the city do not exist. Please check the parameters.");
                    return response;
                }

            } catch (DataAccessException e) { //We should have one error string in a real application to avoid multiple points maintenance.
                postAdviceErrors.rejectValue("advice", null, "An error occurred while saving the advice. Sorry for the inconvenience. Please contact the developers.");
                logger.error("An error occurred while saving the advice. Sorry for the inconvenience. Please contact the developers.", e);
                response = "Error: " + "An error occurred while saving the advice. Sorry for the inconvenience. Please contact the developers.";
                return response;
            }
        }
        response = "Advice posted successfully! Thank you!";
        return response;
    }

    @Override
    public List<CityAdvice> getCityAdvicesForCityUser(String user, String city) {
        List<CityAdvice> cityAdvices = adviceDao.getListCityAdvicesByCityUser(user, city);
        return cityAdvices;
    }

    @Override
    public List<CityAdvice> getCityAdvicesForCity(String city) {
        List<CityAdvice> cityAdvices = adviceDao.getListCityAdvicesByCity(city);
        return cityAdvices;
    }

    @Override
    public int getUserIdByUsername(String username) {
        return adviceDao.getUserIdByUsername(username);
    }

    @Override
    public void updateAddUserAdvicesRatings(List<CityAdvice> cityAdvices, int userRatedId) {
        updateAddUserAdvicesRatingsTx(cityAdvices, userRatedId);
    }

    @Transactional
    private void updateAddUserAdvicesRatingsTx(List<CityAdvice> cityAdvices, int userRatedId) {
        adviceDao.updateAddUserAdvicesRatings(cityAdvices, userRatedId);
        int userId = Utilities.retrieveExcludedUserId(cityAdvices);
        logger.debug("Found user to be excluded:" + userId);
        if (userId != -1) {
            adviceDao.updateUserVisibilityExclusion(userRatedId, userId);
        }
    }

    @Override
    public List<Integer> getRatingOptions() {
        List<Integer> ratingOptions = new ArrayList();
        ratingOptions.add(0);
        ratingOptions.add(1);
        ratingOptions.add(2);
        ratingOptions.add(3);
        ratingOptions.add(4);
        return ratingOptions;
    }

    @Override
    public List<Map<String, Object>> getTagsKeywords() {
        return adviceDao.getTagsKeywords();
    }
}

